/**
 * 实现残局信息的读取
 * 创建者：Carburn Ashroom
 * 2025.6.18
 */

#include "chess.h"
#include "code.h"

namespace Chess {

    QString Message::html() const
    {
        QString result;
        QTextStream t_str {&result};
        QString win_class;
        for (const auto& p : res_name)
            if (p.second == win) {
                win_class = p.first;
                break;
            }
        t_str << QString((2+2)*4, ' ') << R"(<td class=")" << win_class << R"(">)" << attack->name() << QString{"对"} << defence->name() << "</td>\n";
        t_str << QString((2+2)*4, ' ') << R"(<td class=")" << win_class << R"(">)" << win << "</td>\n";
        t_str << QString((2+2)*4, ' ') << R"(<td class="diff)" << difficulty << R"(">)" << diff_name[difficulty] << "</td>\n";
        t_str << QString((2+2)*4, ' ') << R"(<td class=")" << win_class << R"(">)" << strategy << "</td>\n";
        return result;
    }

    bool Message::selected(const Code::Selector& selector) const
    {
        if (not attack->match(selector.attack) or not defence->match(selector.defence))
            return false;
        else if (difficulty<selector.min_diff or difficulty>selector.max_diff)
            return false;
        else if (not selector.win and win==res_win.first())
            return false;
        else if (not selector.draw and win==res_draw.first())
            return false;
        else
            return not (not selector.disputed and win==res_disputed.first());
    }

    void Message::read(QString&& line)
    {
        QStringList messages;
        for (auto m : line.split('/'))
            messages.append(m.trimmed());
        if (messages.count() < 4)
            throw Format_error{"缺失必要信息",std::move(line)};
        QStringList chess {messages[0].split('-')};
        if (chess.count() != 2)
            throw Format_error{"子力格式错误，需要以减号-分隔",std::move(line)};
        attack = Pieces{chess.first()};
        defence = Pieces {chess.back()};
        attack->camp(true);
        defence->camp(false);
        if (res_win.indexOf(messages[1]) != -1)
            win = res_win.first();
        else if (res_draw.indexOf(messages[1]) != -1)
            win = res_draw.first();
        else if (res_disputed.indexOf(messages[1]) != -1)
            win = res_disputed.first();
        else
            throw Format_error{QString{"未知的结论类型：%1"}.arg(messages[1]),std::move(line)};
        difficulty = messages[2].toInt();
        if (difficulty<0 or difficulty>=diff_name.count())
            throw Format_error{QString{"错误的难度等级：%1"}.arg(difficulty),std::move(line)};
        strategy = messages[3];
        if (strategy.isEmpty())
            throw Format_error{"思路必填",std::move(line)};
    }

    QString Class::html() const
    {
        QString result;
        QTextStream t_str {&result};
        t_str << QString((2+1)*4, ' ') << "<tr>\n";
        t_str << QString((2+2)*4, ' ') << R"(<td class="class" rowspan=")" << messages.size() << R"(">)" << name_class() << QString{"类"} << "</td>\n";
        bool tr {};
        for (auto& m : messages) {
            if (not tr)
                tr = true;
            else
                t_str << QString((2+1)*4, ' ') << "<tr>\n";
            t_str << m.html();
            t_str << QString((2+1)*4, ' ') << "</tr>\n";
        }
        return result;
    }

    QString Class::text() const
    {
        QString result;
        QTextStream t_str {&result};
        t_str << "// " <<  name_class() << QString{"类\n"};
        for (auto& m : messages)
            t_str << m.text();
        return result;
    }

    QList<Message> Class::select(const Code::Selector& selector) const
    {
        QList<Message> res;
        for (const auto& m : messages)
            if (m.selected(selector))
                res.append(m);
        return res;
    }

    bool Pieces::contains(const Code::Pieces& chess) const
    {
        if (chess.is_clear())
            return true;
        for (const auto& p : chess.const_pieces())
            if (not contains(p.first, p.second))
                return false;
        return true;
    }

    Class_enum Pieces::type() const
    {
        bool rook {};
        bool horse {};
        bool cannon {};
        bool pawn {};
        for (const auto& p : const_pieces())
            if (QString{"车"} == p.first)
                rook = true;
            else if (QString{"马"} == p.first)
                horse = true;
            else if (QString{"炮"} == p.first)
                cannon = true;
            else if (QString{"兵卒"}.contains(p.first))
                pawn = true;
        if (rook) {
            if (horse)
                return (cannon) ? Class_enum::comp : Class_enum::rhs;
            else if (cannon)
                return Class_enum::rcs;
            else
                return (pawn) ? Class_enum::rp : Class_enum::rook;
        }
        else if (horse) {
            if (cannon)
                return Class_enum::hcs;
            else
                return (pawn) ? Class_enum::hp : Class_enum::horse;
        }
        else if (cannon)
            return (pawn) ? Class_enum::cp : Class_enum::cannon;
        else
            return (pawn) ? Class_enum::pawn : Class_enum::empty;
    }

    void Pieces::analyse()
    {
        int count {1};
        for (unsigned pos{}; pos!=total.size(); ++pos)
            if (QString{"车马炮相兵象卒"}.contains(total[pos])) {
                set_chess(total[pos], count);
                count = 1;
            }
            else if (total[pos]==QString{"仕"} or total[pos]==QString{"士"}) {
                QString mid {total.mid(pos+1, 2)};
                if (mid==QString{"相全"} or mid==QString{"象全"}) {
                    set_chess(QString{"仕相"}, 2);
                    pos += 2;
                }
                else {
                    set_chess(total[pos], count);
                    count = 1;
                }
            }
            else if (QString{"双三四五"}.contains(total[pos]))
                count = QString{"双三四五"}.indexOf(total[pos])+2;
            else if (total.mid(pos, 2) == QString{"高低"}) {
                count = 2;
                ++pos;
            }
            else if (total.mid(pos, 3) == QString{"十六子"}) {
                set_chess(QString{"车马相仕炮"}, 2);
                set_chess(QString{"兵"}, 5);
                pos += 2;
            }
            else if (total[pos] == QString{"缺"}) {
                QChar lost {total[++pos]};
                if (lost==QString{"仕"} or lost==QString{"士"}) {
                    set_chess(QString{"仕"}, 1);
                    set_chess(QString{"相"}, 2);
                }
                else if (lost==QString{"相"} or lost==QString{"象"}) {
                    set_chess(QString{"仕"}, 2);
                    set_chess(QString{"相"}, 1);
                }
                else
                    throw Format_error{QString{"无法解析的子力：缺%1"}.arg(lost)};
            }
    }

    void Pieces::camp(bool red)
    {
        if (red) {
            for (auto& p : avail_pieces()) {
                auto index = chess_black.indexOf(p.first);
                if (index != -1)
                    p.first = chess_red[index];
            }
            for (int i{}; i!=chess_black.size(); ++i)
                total.replace(chess_black[i], chess_red[i]);
        }
        else {
            for (auto& p : avail_pieces()) {
                auto index = chess_red.indexOf(p.first);
                if (index != -1)
                    p.first = chess_black[index];
            }
            for (int i{}; i!=chess_black.size(); ++i)
                total.replace(chess_red[i], chess_black[i]);
        }
    }

    bool Pieces::match(const Code::Pieces& other) const
    {
        if (other.is_accurate())
            return (*this)==other;
        else
            return contains(other);
    }

    bool Pieces::operator==(const Code::Pieces& other) const
    {
        const auto& pieces = const_pieces();
        if (other.is_clear())
            return pieces.isEmpty();
        if (pieces.count() != other.const_pieces().count())
            return false;
        for (const auto& self : pieces) {
            bool find {};
            for (const auto& oth : other.const_pieces())
                if (oth == self) {
                    find = true;
                    break;
                }
            if (not find)
                return false;
        }
        return true;
    }

}
